Private

Function VolumeToDecibels:Int(vol#)
	If vol=>1 Then Return 0;
	If vol=<0 Then Return -10000
	Local Result:Int = Log10(vol) * 3321.928094887
	If Result<-10000 Then Return -10000
	Return Result
EndFunction

Public

atexit_ TDirectSoundAudioStream.Shutdown

Rem
bbdoc: Provides streaming for the DirectSound audio driver. 
End Rem
Type TDirectSoundAudioStream Extends TAudioStream

	Global _dsound:IDirectSound
	Field pDSB:IDirectSoundBuffer
	Global buffersize:Int = 44100*3

	Function ShutDown()
		If _dsound _dsound.Release_
	EndFunction

	Function Build:TDirectSoundAudioStream(Music:TMusic,Paused:Int)
		Local This:TDirectSoundAudioStream = New TDirectSoundAudioStream
		This.Music = Music
		This.Music.PlayStatus = True
		GCSuspend
		Local lpvWrite:Byte Ptr, dwLength:Int, lpvWrite1:Byte Ptr, dwLength1:Int
	    If This.pDSB.Lock(0,this.buffersize,lpvWrite,dwLength,lpvWrite1,dwLength1,DSBLOCK_ENTIREBUFFER) = 0 
			This.Music.FillBuffer( Int(lpvWrite), dwLength )
			This.Music.FillBuffer( Int(lpvWrite1), dwLength1 )
	        This.pDSB.UnLock(lpvWrite,dwLength,lpvWrite1,dwLength1)
 	   	EndIf
		This.SetPaused(Paused)
		GCResume
		Return This
	EndFunction

	Method Startup:Int()
		If DirectSoundCreate( Null,_dsound,Null )>=0
			If _dsound.SetCooperativeLevel( GetDesktopWindow(),DSSCL_PRIORITY )>=0
				Return True
			EndIf
			_dsound.Release_
		EndIf
	EndMethod

	Method New()
	
		GCSuspend
	
		If Not _dsound Startup

		Local hertz:Int=44100
		Local chans:Int=2'ChannelsPerSample[format]
		Local bps:Int=2'BytesPerSample[format]/chans
'		Local size=length*chans*bps
		
		Local fmt:WAVEFORMATEX=New WAVEFORMATEX	
		fmt.wFormatTag=1
		fmt.nChannels=chans
		fmt.nSamplesPerSec=hertz
		fmt.wBitsPerSample=bps*8
		fmt.nBlockAlign=fmt.wBitsPerSample/8*fmt.nChannels
		fmt.nAvgBytesPerSec=hertz*fmt.nBlockAlign
		
		Local desc:DSBUFFERDESC=New DSBUFFERDESC
		desc.dwSize=SizeOf(desc)
		desc.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_CTRLPAN|DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY|DSBCAPS_GETCURRENTPOSITION2 'DSBCAPS_LOCDEFER
		desc.dwBufferBytes=buffersize
		desc.lpwfxFormat=fmt
		
		_dsound.CreateSoundBuffer( desc, pDSB,Null )
		'If pDSB Print "created directsound stream"
		
		GCResume

	EndMethod

	Field finloop:Int, finpos:Int=-1, finstart:Int
	Method Update()

		Local lpvWrite:Byte Ptr, dwLength:Int, lpvWrite1:Byte Ptr, dwLength1:Int
		Local PlayPos:Int, WritePos1:Int, Comp:Int=False
		pDSB.GetCurrentPosition(PlayPos,WritePos1)
		
		If finpos=>0
			If PlayPos<finstart And finloop=1 Then finloop=0 
			If PlayPos=>finpos And finloop=0 Then Stop ; Return
		EndIf
		
		Local Length:Int = PlayPos-WritePos
		If PlayPos<WritePos Then Length = (buffersize - WritePos)+playpos ; comp=True
		Local status:Int ; If pDSB.GetStatus(status) ; If Status=DSBSTATUS_BUFFERLOST Then pDSB.Restore()
		
		If pDSB.Lock(WritePos,Length,lpvWrite,dwLength,lpvWrite1,dwLength1,0) = 0
			If Music.Geti(MM_ISPLAYING)
				If dwLength Then  Music.FillBuffer( Int(lpvWrite), dwLength ) 		; WritePos:+dwLength
				If dwLength1 Then Music.FillBuffer( Int(lpvWrite1), dwLength1 ) 	; WritePos:+dwLength1
			Else
				MemClear(Byte Ptr(lpvWrite),dwLength)
				MemClear(Byte Ptr(lpvWrite),dwLength1)
			EndIf
			pDSB.UnLock(lpvWrite,dwLength,lpvWrite1,dwLength1)
		EndIf

		If Comp=True Then WritePos=playpos

		If Music.Geti(MM_ISPLAYING)=False
			If finpos=-1
				If WritePos=<PlayPos Then finloop=1
				finpos=WritePos
				finstart=playpos
			EndIf
		EndIf

		'If Music.Geti(MM_ISPLAYING)=False And comp=True Then Stop

	EndMethod

	Method Stop()
		If Not pDSB Return
		RemoveHook EmitEventHook,eventhook,Self
		pDSB.Stop
		If Not KeepMusic Then Music.Free
		Music = Null
		pDSB.Release_
		pDSB=Null
	EndMethod

	Method SetPaused( paused:Int )
		If Not pDSB Return
		If paused
			Self.Paused = paused
			pDSB.Stop
		Else
			Music.PlayStatus = True
			pDSB.Play(0,0,DSBPLAY_LOOPING)
		EndIf
	End Method

	Method SetVolume( volume# )
		If pDSB Then pDSB.SetVolume(VolumeToDecibels(volume))
	End Method

	Method SetPan( pan# ) 
		pan=Min(Max(pan,-1),1)
		If pDSB Then pDSB.SetPan pan*10000
	End Method

	Method SetDepth( depth# )
	End Method

	Method SetRate( rate# )
		If pDSB Then pDSB.SetFrequency(samplerate*rate)
	End Method
	
	Method Playing:Int()
		If Not pDSB Return False
		Local status:Int
		pDSB.GetStatus(Status)
		If status & DSBSTATUS_PLAYING Return True
	End Method
				
EndType


